<template>
  <el-dialog
    title="调整场次顺序"
    v-model="dialogVisible"
    :close-on-click-modal="false"
    width="900px"
  >
    <div class="dialog-wrapper-drag">
      <table-custom
        class="table-sort"
        :table-data="matchData"
        :columns="columns"
        :show-operations="writeable"
        :operation-width="130"
      >
        <template v-if="writeable" #operation="{ row, $index }">
          <div class="operation-buttons">
            <el-icon
              class="drag-handle"
              @mousedown="(e) => startDrag($index, e)"
            >
              <Rank />
            </el-icon>
            <el-button
              type="primary"
              :icon="ArrowUp"
              circle
              plain
              size="small"
              :disabled="$index === 0"
              @click="handleMove($index, 'up')"
            />
            <el-button
              type="primary"
              :icon="ArrowDown"
              circle
              plain
              size="small"
              :disabled="$index === matchData.length - 1"
              @click="handleMove($index, 'down')"
            />
          </div>
        </template>
      </table-custom>
    </div>
    <template v-if="writeable" #footer>
      <span class="dialog-footer">
        <el-button @click="handleCancel">取消</el-button>
        <el-button type="primary" @click="handleConfirm">确认</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, defineProps, defineEmits, watch, computed } from 'vue'
import { ArrowUp, ArrowDown, Rank } from '@element-plus/icons-vue'
import tableCustom from './tableCustom.vue'
import { ElMessage, ElLoading } from 'element-plus'
import { typeStatus } from '@/config/type'
import { groupListGenerate, groupListSort } from '@/utils/group'
import {
  fireApi,
  logisticsApi,
  gobangApi,
  obstacleApi,
  droneApi,
} from '@/api/modules'
const apiHandle = {
  fire: fireApi,
  logistics: logisticsApi,
  gobang: gobangApi,
  obstacle: obstacleApi,
  drone: droneApi,
}

const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false,
  },
  writeable: {
    type: Boolean,
    default: false,
  },
  status: {
    type: String,
    default: '',
  },
  selectStatus: {
    type: String,
    default: '',
  },
  knockoutRound: {
    type: Number,
  },
  activeTab: {
    type: String,
    default: '',
  },
  batchNo: {
    type: String,
    default: '',
  },
  groupCategory: {
    type: String,
    default: '',
  },
  playType: {
    type: Number,
    default: '',
  },
  row: {
    type: Object,
    default: () => ({}),
  },
})

const columns = computed(() => {
  if (['fire', 'logistics'].includes(props.activeTab)) {
    return [
      { prop: 'id', label: 'id', width: 60 },
      { prop: 'studentInfo', label: '学生信息' },
      { prop: 'matchSequence', label: '比赛序号', width: 90 },
      { prop: 'groupCategory', label: '组别', width: 80 },
      { prop: 'statusText', label: '比赛阶段', width: 90 },
    ]
  } else {
    return [
      { prop: 'id', label: 'id', width: 60 },
      { prop: 'studentInfo', label: '学生信息', width: 120 },
      { prop: 'opponentId', label: '对手id', width: 70 },
      { prop: 'opponentStudentInfo', label: '对手学生信息', width: 120 },
      { prop: 'groupCategory', label: '组别', width: 80 },
      { prop: 'statusText', label: '比赛阶段', width: 90 },
      { prop: 'groupNo', label: '小组赛组号', width: 100 },
      { prop: 'positionNo', label: '抢位赛组号', width: 100 },
      { prop: 'knockoutNo', label: '淘汰赛组号', width: 100 },
    ]
  }
})

const emit = defineEmits(['update:modelValue', 'confirm'])
const dialogVisible = ref(props.modelValue)
const matchData = ref([])

watch(
  () => props.modelValue,
  (newVal) => {
    dialogVisible.value = newVal
    if (newVal) {
      getTableData()
    }
  }
)

watch(
  () => dialogVisible.value,
  (newVal) => {
    emit('update:modelValue', newVal)
  }
)

// 不同阶段根据不同的字段排序，返回字段名称
const groupNpmberField = computed(() => {
  const groupNumberMap = {
    start: 'matchSequence', // 灭火和物流搬运赛
    position: 'positionNo',
    knockout: 'knockoutNo',
    thirdPlace: 'knockoutNo',
    final: 'knockoutNo',
  }
  return groupNumberMap[props.status]
})
let groupNumberIndexMap = [] // 记录原数据的组号顺序
const handleMove = (index, direction) => {
  // 交换行
  const newIndex = direction === 'up' ? index - 1 : index + 1
  const temp = matchData.value[index]
  matchData.value[index] = matchData.value[newIndex]
  matchData.value[newIndex] = temp

  // 调整组号，保持组号顺序不变，即：以组号排序，改变数据组号，完成排序逻辑
  matchData.value[index][groupNpmberField.value] = groupNumberIndexMap[index]
  matchData.value[newIndex][groupNpmberField.value] =
    groupNumberIndexMap[newIndex]
}

// 拖拽相关状态
const dragState = ref({
  isDragging: false,
  currentIndex: null,
  targetIndex: null,
  startY: 0,
  offsetY: 0,
  ghostElement: null, // 添加空白行元素引用
  ghostStyle: {
    // 添加空白行样式
    display: 'none',
    position: 'absolute',
    width: '100%',
    height: '50px',
    background: '#f5f7fa',
    border: '1px dashed #409eff',
    pointerEvents: 'none',
    zIndex: 9999,
    left: 0,
    top: 0,
  },
})

const startDrag = (index, event) => {
  if (!event) return

  // 获取弹窗相对于视口的位置
  const dialog = document.querySelector('.dialog-wrapper-drag')
  const dialogRect = dialog.getBoundingClientRect()

  // 创建空白行元素
  const ghost = document.createElement('div')
  ghost.className = 'drag-ghost'
  Object.assign(ghost.style, dragState.value.ghostStyle)
  dialog.appendChild(ghost)

  dragState.value = {
    ...dragState.value,
    isDragging: true,
    currentIndex: index,
    targetIndex: index,
    startY: event.clientY,
    offsetY: 0,
    ghostElement: ghost,
    dialogOffsetTop: dialogRect.top, // 保存弹窗顶部位置
    dialogOffsetLeft: dialogRect.left, // 保存弹窗左侧位置
  }

  document.addEventListener('mousemove', handleDragMove)
  document.addEventListener('mouseup', handleDragEnd)
}

const handleDragMove = (event) => {
  if (!dragState.value.isDragging) return

  const { currentIndex, startY, ghostElement, dialogOffsetTop } =
    dragState.value
  const offsetY = event.clientY - startY
  const rowHeight = 50
  const targetIndex = Math.round(currentIndex + offsetY / rowHeight)

  // 更新空白行位置（相对于弹窗）
  if (ghostElement) {
    ghostElement.style.display = 'block'
    // 计算相对于弹窗的top位置
    const relativeTop = event.clientY - dialogOffsetTop - 25 // 25是半行高
    ghostElement.style.top = `${Math.max(0, relativeTop)}px`
  }

  dragState.value = {
    ...dragState.value,
    offsetY,
    targetIndex: Math.max(0, Math.min(matchData.value.length - 1, targetIndex)),
  }
}

const handleDragEnd = () => {
  if (!dragState.value.isDragging) return

  const { currentIndex, targetIndex, ghostElement } = dragState.value

  // 移除空白行
  if (ghostElement && ghostElement.parentNode) {
    ghostElement.parentNode.removeChild(ghostElement)
  }

  if (currentIndex !== targetIndex) {
    const newData = [...matchData.value]
    const [removed] = newData.splice(currentIndex, 1)
    newData.splice(targetIndex, 0, removed)

    // 非小组赛阶段 调整组号
    if (
      ['position', 'knockout', 'thirdPlace', 'final', 'start'].includes(
        props.status
      )
    ) {
      // 调整组号
      const [small, big] = [
        Math.min(currentIndex, targetIndex),
        Math.max(currentIndex, targetIndex),
      ]
      for (let i = small; i <= big; i++) {
        // 拖拽只调整数据顺序，手动维持组号不变，即：以组号排序，改变数据组号，完成排序逻辑
        newData[i][groupNpmberField.value] = groupNumberIndexMap[i]
      }
    }

    matchData.value = newData
  }

  dragState.value = {
    ...dragState.value,
    isDragging: false,
    currentIndex: null,
    targetIndex: null,
    startY: 0,
    offsetY: 0,
    ghostElement: null,
  }

  document.removeEventListener('mousemove', handleDragMove)
  document.removeEventListener('mouseup', handleDragEnd)
}

const handleCancel = () => {
  emit('update:modelValue', false)
}

const getGroupList = async (loading) => {
  const res = await apiHandle[props.activeTab].goupPersonQuery.post({
    regNo: props.row.regNo,
    batchNo: props.batchNo,
    groupCategory: props.groupCategory,
    playType: props.playType,
  })
  loading.close()
  if (res.code !== 200) {
    ElMessage.error('查询错误!')
    return
  }

  res.data.forEach((item) => {
    item.statusText = typeStatus[item.status]
  })
  matchData.value = groupListGenerate(res.data)
}
const getTableData = async () => {
  matchData.value = []
  const loading = ElLoading.service({
    text: 'Loading',
    spinner: 'el-icon-loading',
    background: 'rgba(0, 0, 0, 0.7)',
  })
  if (props.status === 'group') {
    // 小组赛
    getGroupList(loading)
    return
  }

  const params = {
    pageNum: 1,
    pageSize: 100,
    batchNo: props.batchNo,
    groupCategory: props.groupCategory,
    status: props.status,
    playType: props.playType,
  }

  if (props.status === 'knockout' && props.selectStatus === 'knockout') {
    params.knockoutRound = props.knockoutRound
  }
  const res = await apiHandle[props.activeTab].queryPageList.post(params)
  loading.close()
  if (res.code !== 200) {
    ElMessage.error(res.message || '获取数据失败')
    return
  }
  if (['logistics', 'fire'].includes(props.activeTab)) {
    matchData.value = res.data.list
  } else {
    // 原数据是每人一条，现在需要根据比赛将比赛双方两两合成一条
    const dataMap = {}
    matchData.value = res.data.list
      // 过滤掉组号为-1的比赛
      .filter((item) => item[groupNpmberField.value] !== -1)
      .reduce((acc, cur) => {
        // 找出对手双方 在dataMap中是否存在，不存在就创建，存在就将对手信息添加到dataMap中
        // 同时按照遍历顺序将数据添加到acc中，保证排序正确
        const groupNo = cur[groupNpmberField.value]
        if (dataMap[groupNo] === undefined) {
          dataMap[groupNo] = { ...cur }
          acc.push(dataMap[groupNo])
        } else {
          dataMap[groupNo].opponentId = cur.id
          dataMap[groupNo].opponentStudentInfo = cur.studentInfo
        }
        return acc
      }, [])
  }
  groupNumberIndexMap = matchData.value.map((item, index) => {
    item.statusText = typeStatus[item.status]
    return item[groupNpmberField.value]
  })
}
const handleConfirm = async () => {
  const loading = ElLoading.service({
    text: 'Loading',
    spinner: 'el-icon-loading',
    background: 'rgba(0, 0, 0, 0.7)',
  })
  const res = await apiHandle[props.activeTab].scoreUpdate.post(
    props.status === 'group'
      ? groupListSort(matchData.value)
      : matchData.value.reduce((arr, item) => {
          const newData = { id: item.id }
          newData[groupNpmberField.value] = item[groupNpmberField.value]
          arr.push(newData)
          if (!['logistics', 'fire'].includes(props.activeTab)) {
            arr.push({
              id: item.opponentId,
              [groupNpmberField.value]: item[groupNpmberField.value],
            })
          }
          return arr
        }, [])
  )
  loading.close()
  if (res.code !== 200) {
    ElMessage.error('保存错误!')
    return
  }
  ElMessage.success('保存成功!')

  emit('confirm', matchData.value)
  emit('update:modelValue', false)
}
</script>

<style lang="scss" scoped>
.el-dialog tbody tr {
  &:first-child .operation-buttons button:nth-child(2),
  &:last-child .operation-buttons button:last-child {
    visibility: hidden;
  }
}
.table-sort {
  position: relative;
}
.operation-buttons {
  display: flex;
  gap: 8px;
  justify-content: center;
  align-self: center;
  align-items: center;
  user-select: none;
  .el-button {
    min-width: 0;
    height: 24px;
  }
}
.drag-handle {
  cursor: move;
  margin-right: 8px;
  color: #606266;
  font-size: 16px;

  &:active {
    color: #409eff;
    transform: scale(1.2);
  }
  &:hover {
    color: #409eff;
  }
}
.dialog-wrapper-drag {
  position: relative;
}

.drag-ghost {
  position: absolute;
  width: calc(100% - 32px); // 减去可能的padding
  height: 50px;
  background: #f5f7fa;
  border: 1px dashed #409eff;
  pointer-events: none;
  z-index: 9999;
  left: 16px; // 与表格对齐
}
</style>
